/*
 * @ : Copyright (c) 2021 Phytium Information Technology, Inc. 
 *  
 * SPDX-License-Identifier: Apache-2.0.
 * 
 * @Date: 2021-08-23 16:22:57
 * @LastEditTime: 2021-08-26 10:42:24
 * @Description:  This files is for 
 * 
 * @Modify History: 
 *  Ver   Who        Date         Changes
 * ----- ------     --------    --------------------------------------
 */
#include "ft_assert.h"
#include "fsleep.h"
#include "gmac.h"
#include "gmac_hw.h"
#include "phy_common.h"

extern u32 GmacPhyWriteReg(GmacCtrl *pCtrl, u16 phyReg, u32 regVal);
extern u32 GmacPhyReadReg(GmacCtrl *pCtrl, u16 phyReg, u32 *pRegVal);
extern u32 GmacPhyInit(GmacCtrl *pCtrl);

/**
 * @name: GmacSetMacAddr
 * @msg: set mac addrress
 * @return {*}
 * @param {GmacCtrl} *pCtrl
 * @param {u8} pAddr
 */
static void GmacSetMacAddr(GmacCtrl *pCtrl, const u8 pAddr[GMAC_MAC_ADDR_LEN])
{
    FT_ASSERTVOID(pCtrl && pAddr);
    u32 regVal;

    regVal = ((u32)pAddr[5] << 8) | (u32)pAddr[4];
    GMAC_WRITE_REG(pCtrl, GMAC_MAC_ADDR0_UPPER16BIT_OFFSET, regVal);
    regVal = ((u32)pAddr[3] << 24) | ((u32)pAddr[2] << 16) | ((u32)pAddr[1] << 8) | pAddr[0];
    GMAC_WRITE_REG(pCtrl, GMAC_MAC_ADDR0_LOWER32BIT_OFFSET, regVal);

    return;
}

/**
 * @name: GmacGetMacAddr
 * @msg: get mac addrress
 * @return {*}
 * @param {GmacCtrl} *pCtrl
 * @param {u8} pAddr
 */
static void GmacGetMacAddr(GmacCtrl *pCtrl, u8 pAddr[GMAC_MAC_ADDR_LEN])
{
    FT_ASSERTVOID(pCtrl && pAddr);
    u32 macHigh = GMAC_READ_REG(pCtrl, GMAC_MAC_ADDR0_UPPER16BIT_OFFSET);
    u32 macLow = GMAC_READ_REG(pCtrl, GMAC_MAC_ADDR0_LOWER32BIT_OFFSET);

    /* get lower 32 bits of mac addr */
	pAddr[0] = (u8)(macLow & 0xff);
	pAddr[1] = (u8)((macLow >> 8) & 0xff);
	pAddr[2] = (u8)((macLow >> 16) & 0xff);
	pAddr[3] = (u8)((macLow >> 24) & 0xff);

    /* get upper 16 bits of mac addr */
	pAddr[4] = (u8)(macHigh & 0xff);
	pAddr[5] = (u8)((macHigh >> 8) & 0xff);

    return;
}

/**
 * @name: GmacMacDmaInit
 * @msg: init gmac and dma ctrl register
 * @return {*}
 * @param {GmacCtrl} *pCtrl
 */
static u32 GmacMacDmaInit(GmacCtrl *pCtrl)
{
    FT_ASSERTZERONUM(pCtrl);
    u32 regVal;
    u32 ret = GMAC_SUCCESS;
    GmacConfig *pConfig = &pCtrl->config;

    ret = GmacPhyGetAddr(pCtrl);
    if (GMAC_SUCCESS != ret)
    {
        GMAC_ERROR("get phy addr failed");
        return ret;
    }

    /********ctrl reg*********/
    /* Set the WD bit according to ETH Watchdog value */
    /* Set the JD: bit according to ETH Jabber value */
    /* Set the IFG bit according to ETH InterFrameGap value */
    /* Set the DCRS bit according to ETH CarrierSense value */
    /* Set the FES bit according to ETH Speed value */
    /* Set the DO bit according to ETH ReceiveOwn value */
    /* Set the LM bit according to ETH LoopbackMode value */
    /* Set the DM bit according to ETH Mode value */
    /* Set the IPCO bit according to ETH ChecksumOffload value */
    /* Set the DR bit according to ETH RetryTransmission value */
    /* Set the ACS bit according to ETH AutomaticPadCRCStrip value */
    /* Set the BL bit according to ETH BackOffLimit value */
    /* Set the DC bit according to ETH DeferralCheck value */    
    regVal  = GMAC_READ_REG(pCtrl, GMAC_CONF_OFFSET);
    regVal &= ~(GMAC_CONF_WD | GMAC_CONF_JD | GMAC_CONF_DCRS | GMAC_CONF_DO |
                GMAC_CONF_LOOPBACK_MODE);
    regVal |= GMAC_CONF_IFG(0);

    if (GMAC_CHECKSUM_BY_HARDWARE == pConfig->checkSumMode)
        regVal |= GMAC_CONF_IPC;
    else
        regVal &= ~GMAC_CONF_IPC;

    regVal |= GMAC_CONF_DISABLE_RETRY;
    regVal &= ~GMAC_CONF_ACS;
    regVal |= GMAC_CONF_BL(0);
    regVal &= ~GMAC_CONF_DC;

    if (GMAC_MODE_FULLDUPLEX == pConfig->duplexMode)
        regVal |= GMAC_CONF_DUPLEX_MODE;
    else
        regVal &= ~GMAC_CONF_DUPLEX_MODE;

    regVal &= ~GMAC_CONF_FES;
    GMAC_WRITE_REG(pCtrl, GMAC_CONF_OFFSET, regVal);
    fsleep_microsec(PHY_DELAY_US);

    /********filter reg*********/
    /* Set the RA bit according to ETH ReceiveAll value */
    /* Set the SAF and SAIF bits according to ETH SourceAddrFilter value */
    /* Set the PCF bit according to ETH PassControlFrames value */
    /* Set the DBF bit according to ETH BroadcastFramesReception value */
    /* Set the DAIF bit according to ETH DestinationAddrFilter value */
    /* Set the PR bit according to ETH PromiscuousMode value */
    /* Set the PM, HMC and HPF bits according to ETH MulticastFramesFilter value */
    /* Set the HUC and HPF bits according to ETH UnicastFramesFilter value */    
    regVal  = GMAC_READ_REG(pCtrl, GMAC_FRAME_FILTER_OFFSET);
    regVal |= GMAC_FRAME_FILTER_RA;
    regVal |= GMAC_FRAME_FILTER_PCF(2);
    regVal &= ~GMAC_FRAME_FILTER_PM;
    regVal &= ~GMAC_FRAME_FILTER_DBF;
    regVal &= ~GMAC_FRAME_FILTER_DAIF;
    regVal &= ~GMAC_FRAME_FILTER_PR;  
    GMAC_WRITE_REG(pCtrl, GMAC_FRAME_FILTER_OFFSET, regVal);
    fsleep_microsec(PHY_DELAY_US);     

    /********hash reg*********/ 
    GMAC_WRITE_REG(pCtrl, GMAC_HASH_HIGH_OFFSET, 0x0);
    fsleep_microsec(PHY_DELAY_US);      
    GMAC_WRITE_REG(pCtrl, GMAC_HASH_LOW_OFFSET, 0x0);
    fsleep_microsec(PHY_DELAY_US); 

    /********flow ctrl reg*********/
    /* Set the PT bit according to ETH PauseTime value */
    /* Set the DZPQ bit according to ETH ZeroQuantaPause value */
    /* Set the PLT bit according to ETH PauseLowThreshold value */
    /* Set the UP bit according to ETH UnicastPauseFrameDetect value */
    /* Set the RFE bit according to ETH ReceiveFlowControl value */
    /* Set the TFE bit according to ETH TransmitFlowControl value */    
    regVal  = GMAC_READ_REG(pCtrl, GMAC_FLOW_CTRL_OFFSET);
    regVal |= GMAC_FLOW_DZPQ;
    regVal |= GMAC_FLOW_PLT(0);
    regVal &= ~GMAC_FLOW_RFE;
    regVal &= ~GMAC_FLOW_TFE;
    GMAC_WRITE_REG(pCtrl, GMAC_FLOW_CTRL_OFFSET, regVal);
    fsleep_microsec(PHY_DELAY_US);

    /********vlan tag reg*********/ 
    /* Set the ETV bit according to ETH VLANTagComparison value */
    /* Set the VL bit according to ETH VLANTagIdentifier value */     
    regVal = GMAC_VLAN_TAG_VL(0);
    regVal &= ~GMAC_VLAN_TAG_ETV; 
    GMAC_WRITE_REG(pCtrl, GMAC_VLAN_TAG_OFFSET, regVal);
    fsleep_microsec(PHY_DELAY_US);

    /********dma op reg*********/
    /* Set the DT bit according to ETH DropTCPIPChecksumErrorFrame value */
    /* Set the RSF bit according to ETH ReceiveStoreForward value */
    /* Set the DFF bit according to ETH FlushReceivedFrame value */
    /* Set the TSF bit according to ETH TransmitStoreForward value */
    /* Set the TTC bit according to ETH TransmitThresholdControl value */
    /* Set the FEF bit according to ETH ForwardErrorFrames value */
    /* Set the FUF bit according to ETH ForwardUndersizedGoodFrames value */
    /* Set the RTC bit according to ETH ReceiveThresholdControl value */
    /* Set the OSF bit according to ETH SecondFrameOperate value */    
    regVal  = GMAC_READ_REG(pCtrl, GMAC_DMA_OP_OFFSET);
    regVal &= GMAC_DMA_OP_CLEAR_MASK;
    regVal &= ~GMAC_DMA_OP_DT;
    regVal |= GMAC_DMA_OP_RSF;
    regVal &= ~GMAC_DMA_OP_DFF;
    regVal |= GMAC_DMA_OP_TSF;
    regVal |= GMAC_DMA_OP_TTC(7);
    regVal |= GMAC_DMA_OP_OSF;
    GMAC_WRITE_REG(pCtrl, GMAC_DMA_OP_OFFSET, regVal);
    fsleep_microsec(PHY_DELAY_US);

    /********dma bus mode reg*********/
    /* Set the AAL bit according to ETH AddressAlignedBeats value */
    /* Set the FB bit according to ETH FixedBurst value */
    /* Set the RPBL and 4*PBL bits according to ETH RxDMABurstLength value */
    /* Set the PBL and 4*PBL bits according to ETH TxDMABurstLength value */
    /* Set the Enhanced DMA descriptors bit according to ETH EnhancedDescriptorFormat value*/
    /* Set the DSL bit according to ETH DesciptorSkipLength value */
    /* Set the PR and DA bits according to ETH DMAArbitration value */    
    regVal  = GMAC_READ_REG(pCtrl, GMAC_DMA_BUS_MODE_OFFSET);
    regVal |= GMAC_DMA_BUS_AAL;
    regVal |= GMAC_DMA_BUS_FB;
    regVal |= GMAC_DMA_BUS_RPBL(32);
    regVal |= GMAC_DMA_BUS_PBL(32);
    regVal |= GMAC_DMA_BUS_ATDS;
    regVal |= GMAC_DMA_BUS_PR(0);
    regVal |= GMAC_DMA_BUS_USP;
    GMAC_WRITE_REG(pCtrl, GMAC_DMA_BUS_MODE_OFFSET, regVal);
    fsleep_microsec(PHY_DELAY_US); 
    
    GMAC_WRITE_REG(pCtrl, GMAC_DMA_AXI_BUS_MOD_OFFSET, 0x0000000e);
    GmacSetMacAddr(pCtrl, pCtrl->config.macAddr);

    return ret;
}

/**
 * @name: GmacHwInitialize
 * @msg: init gmac register
 * @return {*}
 * @param {GmacCtrl} *pCtrl
 */
u32 GmacHwInitialize(GmacCtrl *pCtrl)
{
    FT_ASSERTZERONUM(pCtrl);
    u32 regVal;
    int timeout = 0;
    u32 ret = GMAC_SUCCESS;
    u8 macAddr[GMAC_MAC_ADDR_LEN];

    GmacGetMacAddr(pCtrl, macAddr); /* Backup mac address before software reset */

    /* Software reset */
    GMAC_SET_BIT(pCtrl, GMAC_DMA_BUS_MODE_OFFSET, GMAC_DMA_BUS_SWR);
    do
    {
        regVal = GMAC_READ_REG(pCtrl, GMAC_DMA_BUS_MODE_OFFSET);
        fsleep_microsec(PHY_DELAY_US);
    } while ((regVal & GMAC_DMA_BUS_SWR) && (GMAC_RETRY_TIMES >= ++timeout));

    if (GMAC_RETRY_TIMES <= timeout)
    {
        GMAC_ERROR("timeout when reset dma");
        return GMAC_ERR_TIMEOUT;
    }

	/*
	 * Soft reset above clears HW address registers.
	 * So we have to set it here once again.
	 */
    GmacSetMacAddr(pCtrl, macAddr);

    GMAC_WRITE_REG(pCtrl, GMAC_DMA_BUS_MODE_OFFSET, GMAC_DMA_BUS_INIT);
    GMAC_SET_BIT(pCtrl, GMAC_DMA_OP_OFFSET, GMAC_DMA_OP_FTF);

    timeout = 0;
    do
    {
        regVal = GMAC_READ_REG(pCtrl, GMAC_DMA_OP_OFFSET);
        fsleep_microsec(PHY_DELAY_US);
    } while ((regVal & GMAC_DMA_OP_FTF) && (GMAC_RETRY_TIMES > ++timeout));

    if (GMAC_RETRY_TIMES <= timeout)
    {
        GMAC_ERROR("time out when flush fifo");
        return GMAC_ERR_TIMEOUT;
    }
    
    GMAC_WRITE_REG(pCtrl, GMAC_CONF_OFFSET, GMAC_CONF_INIT); /* gmac ctrl init */
    GMAC_WRITE_REG(pCtrl, GMAC_FLOW_CTRL_OFFSET, 0x0); /* disable flow control */
    GMAC_W1C_BIT(pCtrl, GMAC_DMA_STATUS_OFFSET, GMAC_DMA_STATUS_CLR_ABLE);
    GMAC_WRITE_REG(pCtrl, GMAC_DMA_INTR_OFFSET, 0x0);

    ret = GmacPhyGetAddr(pCtrl); /* get Phy addr */
    if (GMAC_SUCCESS != ret)
    {
        GMAC_ERROR("failed to get phy addr");
        return ret;
    }

    ret |= GmacPhyInit(pCtrl);
    ret |= GmacMacDmaInit(pCtrl);
    return ret;
}